###############################################################
=begin
Nombre del archivo: RubyServer.rb
Universidad del Valle de Guatemala
Curso: Sistemas Operativos Avanzados
Autor: Martín Guzmán - 08041
Fecha: Septiembre de 2010
Descripción: Servidor Web hecho para el proyecto #1
   - Este proyecto consta de varios módulos.
	- Demonio - correr desde que empieza
	- Módulo de conexión - manejar múltiples conexiones 
	  simultáneas
	- Módulo de interpretación - petición GET
	- Módulo de mapeo - "/usr/www/data"+PETICIÓN
	- Módulo de autorización - Basic Authentication - archivos htmls
	- Módulo de auditoría y registro - log de acceso
	- Módulo de respuesta - forma la petición y revisa caché
		- 5 archivos en caché
=end
###############################################################

require 'socket'
require 'date'
require 'logger'
require 'base64'
require 'rexml/document'
require './Cache'
require './GET_Answer'
require './GET_UVG_Answer'
require './User'
require './Headers'
require './ServerNode'
include REXML

comienzo = "./../"#una arriba de esta

begin
	# Código que puede dar problemas
	doc = Document.new File.new("#{comienzo}config.xml")
	root_file = doc.root
	log_start = root_file.attributes['log_dir'].to_s
	$server_name = root_file.attributes['server_name'].to_s
	$port = root_file.attributes['puerto'].to_i
	$max_cache = root_file.elements["cache"].attributes["cache_max_len"].to_i
	$root = root_file.elements["rootDir"].attributes["root_dir"].to_s

rescue => detail

	log_start = "./"
	$port = 8080
	$max_cache = 5
	$root = "./"
	$server_name = "martin.net"

ensure

	#Add "/" if missing
	if ($root[$root.length-1] != 47)
	  $root << "/"
	end
	
	#Add "/" if missing
	if (log_start[log_start.length-1] != 47)
	  log_start << "/"
	end
	
	$log = Logger.new("#{log_start}access_#{Date.today.day}-#{Date.today.month}-#{Date.today.year}.log")
	$log.level = Logger::DEBUG	
end

$server_list = Array.new
$status = "working"


###############################################################
# local_ip
# obtiene la ip de la máquina donde está montado el servidor
###############################################################
def local_ip
	orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true  # turn off reverse DNS resolution temporarily
	 
	UDPSocket.open do |s|
		s.connect '64.233.187.99', 1
		s.addr.last
	end
	ensure
		Socket.do_not_reverse_lookup = orig
end

$server_ip = local_ip()

begin
	archivo_config = File.open("#{comienzo}config.text", 'r').read().split("\n")
	
	archivo_config.each{ |i|
		print i.split(" ")[1]," : ",i.split(" ")[0],"\n"#TODO quitar este debug
		if i.split(" ")[1] != $server_name
			$server_list << ServerNode.new(i.split(" ")[1], i.split(" ")[0]) #guarda: martin.net => 192.168.1.2
		end
	}
	#$server_list.each {|i| puts i}

rescue => detail
	puts "NO SE PUDO CARGAR EL ARCHIVO DE CONFIGURACION DE HOSTS"
	$server_list << ServerNode.new($server_name,"#{$server_ip}")
end

#arreglo de User
$users = Array.new
#listas para las diferentes acciones
$users_to_add = Array.new
$users_to_delete = Array.new
$users_to_update = Array.new

#======================================================================================================================================================


###############################################################
# Clase Conection
# representa una conexión
###############################################################
class Conection
	def initialize(client)
		
		puts "entra un nuevo request: #{client}"
		
		@client = client
		@client_IP = Socket.unpack_sockaddr_in(client.getpeername)[1] #[0]-> es el port
		puts "entra un nuevo request: #{@client_IP}"
		
		begin
			lineas = @client.recv(1000000000)
			@request, lineas = lineas.split("\n",2)
			@request = httpDecode(@request)
			
			i=0
			concat = ""
			temp = Array.new
			while i < lineas.split("\n").size
				if lineas.split("\n")[i] =~ /.+:.+/ and concat.size != 0
					temp << concat
					concat = lineas.split("\n")[i] + "\n"
				else
					concat += lineas.split("\n")[i] + "\n"
				end
				i+=1
			end
			temp << concat
			
			@headers = Headers.new(temp)
			@headers.print() #TODO quitar este debug
		rescue => details
			puts details
			@request = "GET / HTTP/1.1"
			@headers = Headers.new(Array.new)
		end

	end
	
	
	#------------------------------------------------------
	# httpDecode -> cambia los valores que trae del navegador
	#	para caracteres especiales
	#------------------------------------------------------
	def httpDecode(request)
		temp = request.strip
		return (temp).gsub("%20"," ").gsub("%C3%B1","ñ").gsub("%C3%A1","á").gsub("%C3%A9","é").gsub("%C3%AD","í").gsub("%C3%B3","ó").gsub("%C3%BA","ú")
	end
	
	#------------------------------------------------------
	# map() -> mapea la url hacia un archivo existente
	#------------------------------------------------------
	def map(url)
		#return ($root+"index.html") if(url=="")
		return ($root + url)
	end

	#------------------------------------------------------
	# analizeRequest() -> analiza la solicitud y dependiendo
	# de ella almacena en @answer la respuesta
	#------------------------------------------------------
	def analyzeRequest()
		
		puts "Request entrante: .#{@request}."
		
		case @request

		when /GET .* HTTP\/1\.1/
			
			@answer = GET_Answer.new()
			
			#revisar si no está downtime
			if $status != "down"
				
				era_url_default = analyzeDefaultURL()
				#revisar las url defaults
				if era_url_default == false
					#petición normal
					analyzeNormalRequest()
				end
			else
				#está en downtime
				@answer.setHTTP_code("downtime")
			end
			
			
		when /GET_UVG .* HTTP\/1\.1/
			
			#puts "petición GET_UVG: .#{@request}."#TODO debug
			#puts "server name: #{$server_name}"#TODO debug
			@answer = GET_UVG_Answer.new($server_name)
			
			if @headers.contain("Action")
				analyzeAction()
			else
				#no tiene el header "Action"
				@answer.setHTTP_code("error")
				@answer.addHeader("Problem","GET_UVG request without 'Action' header")
			end
			
		else
			puts "petición no válida #{@request}"
			
			#no es una request de GET ni GET_UVG válido
			@answer = GET_Answer.new()
			@answer.setHTTP_code("Bad Request")
		end
		
		#enviar respuesta
		@client.puts @answer.answer()
	end
	
	
	
	#------------------------------------------------------
	# analyzeNormalRequest() -> la petición es una petición
	# normal... guardar en @answer como el proyecto anterior
	#------------------------------------------------------
	def analyzeNormalRequest()
		
		url_to_check = @request.strip[5..-9].strip
		
		if @headers.contain("Host")
			server_to_request = @headers.get("Host").split(":",2)[0]
			
			if $server_name == server_to_request
				
				analyzeLocalRequest()
				
			else
				
				#ver si lo que se busca está en caché
				if $cache.exist(url_to_check, server_to_request)
					
					fileType = url_to_check.split('.').last
					
					@answer.content = $cache.getContent(url_to_check,$server_name)
					@answer.setHTTP_code("ok",fileType)
					
				else
					#no está en caché, hacer la petición a otro servidor
					#otro servidor es quien se le hace la petición
					server = getServer(server_to_request)
					server.setRequest_to_GET("/#{url_to_check}", @headers.list)
					response = server.sendRequest()
				
					#guardar en caché si la respuesta es 200 OK
					if response.split("\n",2)[0].upcase.include?("200 OK")
						#guardar en caché
					
						headers, content = response.split("\n",2)[1].strip.split("\n\n",2)
					
						$cache.addToCache(url_to_check, server_to_request, content)
					end
				
					@answer.HTTP_code = response
				end
			end
			
		else
			@answer.setHTTP_code("GET request without Host")
		end
		
	end
	
	
	#------------------------------------------------------
	# analyzeLocalRequest() -> revisa la petición localmente
	#------------------------------------------------------
	def analyzeLocalRequest()
		part = @request.strip[5..-9].strip
		url = map(part)
		fileType = url.split('.').last
		
		puts "local request: #{part} - #{fileType} - #{url}"
		
		if existFileInLocal(url) #verificar que exista
			
			if(fileType == "link" and url =~ /.*\//)
			
			
				nueva_dir = getFileInLocal(url).read().strip
				puts "="*20,"."+nueva_dir+".","="*20
				
				if(File.exist?(nueva_dir)) #verificar que exista
				
					url = nueva_dir
					fileType = url.split('.').last
				
				else
				
					puts "devolver que no encontró la dir"
					#no existe el archivo
					@answer.setHTTP_code("link Not Found",fileType)
					return
				
				end
			
			
			end
		
			if(fileType == "htmls")
		
				#si existe y no necesita credenciales
			
				correct = false
				if @headers.contain("Authorization")
					
					credential = @headers.get("Authorization").split(" ",2).last.strip
					puts "credencial que trae: #{credential}"
					puts "credencial que trae: #{Base64::decode64("#{credential}")}"
					correct = existUserAndPsw($users, credential)
					puts "correct: #{correct}"
					puts "$users: #{$users.each{|i| puts i.print()}}"
				end
			
				if(correct)
					#"Credencial aceptada"
					$cache.addToCache(part, $server_name, getFileInLocal(url).read())
					
					@answer.content = $cache.getContent(part,$server_name)
					@answer.setHTTP_code("ok",fileType)
					
				else
					#"Credencial no aceptada"
					@answer.setHTTP_code("Authorization Required",fileType)
				end
		
			#revisar si es carpeta
			elsif(fileType =~ /.*\//)
				#TODO ver qué devolver si es carpeta => en Answer/setHTTP_code/when "folder found"
				@answer.setfolderPage($root,part,url,$port)
				@answer.setHTTP_code("Folder Found",fileType)
			
			else
			
			
				#si existe y no necesita credenciales
				#"La petición no requería credenciales"
				$cache.addToCache(part, $server_name, getFileInLocal(url).read())
				@answer.content = $cache.getContent(part,$server_name)
				@answer.setHTTP_code("OK",fileType)
				puts ""+("-"*50+"\n")+"SI existe #{url}"+"\n"+("-"*50+"\n")#TODO quitar este debug..
			
			
			end


		else
		
			#no existe el archivo
			@answer.setHTTP_code("Not Found",fileType)
			puts ""+("-"*50+"\n")+"NO existe #{url}"+"\n"+("-"*50+"\n")#TODO quitar este debug..


		end
		
	end
	
	
	#------------------------------------------------------
	# analyzeDefaultURL() -> revisa si la petición es alguna
	# de las URL definidas por default... si lo es, manda a
	# hacer la determinada acción (retorna true). De lo contrario retorna
	# false
	#------------------------------------------------------
	def analyzeDefaultURL()
		
		retorno = true
		
		#GET /blabla HTTP/1.1
		url_to_check = @request.strip[5..-9].strip
		
		
		puts "url_to_check: .#{url_to_check}."
		
		case url_to_check
		when /(\/)?create_user\/.+\/.+/
			puts "crear usuario! :)!"
			action, user, psw = url_to_check.split("/",3)
			#puts "separado: #{action} - #{user} - #{psw}"#TODO borrar debug
			send_create_user(user,psw)
			
		when /(\/)?edit_user\/.+\/.+/
			puts "editar usuario! :)!"
			action, user, psw = url_to_check.split("/",3)
			send_edit_user(user,psw)
			
		when /(\/)?delete_user\/.+\/.+/
			puts "borrar usuario! :)!"
			action, user, psw = url_to_check.split("/",3)
			send_delete_user(user,psw)
			
		when /(\/)?down/
			puts "poner en down algún servidor! :)!"
			send_down()
			
		when /(\/)?flush\//
			puts "hacer flush de un archivo!"
			send_flush(url_to_check)
		else
			retorno = false
		end
		
		return retorno
	end
	
	
	#------------------------------------------------------
	# send_flush() -> 
	#------------------------------------------------------
	def send_flush(url_to_check)
		
		if @headers.contain("Host")
			server_to_flush = @headers.get("Host").split(":",2)[0]
			
			if $server_name == server_to_flush
				
				path_parcial = url_to_check.split("/",2)[1]
				path_archivo = map( path_parcial )
				
				if existFileInLocal( path_archivo )
					#el archivo sí existe...
					archivo = getFileInLocal( path_archivo )
					content = archivo.read()
					
					i=0
					while i<$server_list.size
						nodo = $server_list[i]
						nodo.setRequest_to_GET_UVG($server_name)
						nodo.addHeader("Action","update_file")
						nodo.addHeader("Name","#{path_parcial}")
						nodo.addHeader("File","#{content}")
						
						answer = nodo.sendRequest()
					end
					
					$cache.flush(path_parcial, $server_name, content)
					@answer.setHTTP_code_flush( "flush ok", path_parcial )
					
				else
					#no existe el archivo a actualizar...
					@answer.setHTTP_code_flush( "flush don't exist", path_parcial )
				end
				
				
				
			else
				#otro servidor es quien se quiere hacer flush
				server = getServer(server_to_flush)
				server.setRequest_to_GET("/#{url_to_check}", @headers.list)
				response = server.sendRequest()
				@answer.HTTP_code = response
			end
			
		else
			@answer.setHTTP_code("GET request without Host")
		end
		
	end
	
	#------------------------------------------------------
	# existFileInLocal(path) -> revisa si localmente existe el
	# archivo soliticado
	#------------------------------------------------------
	def existFileInLocal(path)
		return File.exist?(path)
	end
	
	
	#------------------------------------------------------
	# getFileInLocal(path) -> revisa si localmente existe el
	# archivo soliticado y lo devuelve
	#------------------------------------------------------
	def getFileInLocal(path)
		begin
			archivo = File.open(path, 'r')
			return archivo
		rescue Errno::ENOENT
			return nil
		end
		return nil
	end
	
	
	#------------------------------------------------------
	# send_down() -> pregunta si es posible hacer down
	# revisa si el servidor actual tiene que hacer down
	#------------------------------------------------------
	def send_down()
		
		if @headers.contain("Host")
			server_to_down = @headers.get("Host").split(":",2)[0]
			
			if $server_name == server_to_down
				#este es el que se quiere poner en down...
				$status = "waiting"
				i=0
				can_down = true
				while i<$server_list.size
					nodo = $server_list[i]
					nodo.setRequest_to_GET_UVG($server_name)
					nodo.addHeader("Action","ask_downtime")
					
					answer = nodo.sendRequest()
					headers_de_respuesta = Headers.new(  (answer.split("\n",2)[1]).split("\n")  )
					
					if headers_de_respuesta.contain("Status")
						status_nodo = headers_de_respuesta.get("Status")
						if status_nodo == "working"
							
						elsif status_nodo == "waiting" or status_nodo == "down"
							can_down = false
							@answer.setHTTP_code_down("can't down", nodo.name)
							break
						end
					else
						#la respuesta no tiene el header status
						can_down = false
						@answer.setHTTP_code_down("error", nodo.name)
						break
					end
					
					i+=1
				end
				
				$status = "working"
				
				if can_down
					$status = "down"
					@answer.setHTTP_code_down("down", $server_name)
				end
				
				
			else
				#otro servidor es quien se quiere poner en down
				server = getServer(server_name)
				server.setRequest_to_GET("/down", @headers.list)
				response = server.sendRequest()
				@answer.HTTP_code = response
			end
			
		else
			@answer.setHTTP_code("GET request without Host")
		end
		
	end
	
	#------------------------------------------------------
	# getServer() -> retorna el servidor con el nombre
	# server_name o nil si no existe
	#------------------------------------------------------
	def getServer(server_name)
		$server_list.each{ |i|
			return i if i.name == server_name
		}
		return nil
	end
	
	#------------------------------------------------------
	# send_create_user() -> envía a todos los servidores la
	# petición de crear al usuario, si corresponde
	#------------------------------------------------------
	def send_create_user(user,psw)
		
		#GET_UVG / HTTP/1.1
		#Host: {ServerName}/8080
		#Action: ask_create_user
		#User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
		
		user_to_create = User.new(user,psw)
		#puts "Usuario a crear: #{user_to_create.print()} = #{user_to_create.get_encode()}"#TODO quitar debug
		encode = user_to_create.get_encode()
		
		if existUser($users,encode)
			#user already exist
			@answer.setHTTP_code_Users("user exist","create","#{user_to_create.name}")
		elsif existUser($users_to_add,encode) or existUser($users_to_delete,encode) or existUser($users_to_update,encode)
			#user in proccess, cant create
			@answer.setHTTP_code_Users("user progress","create","#{user_to_create.name}")
		else
			#no existe en ninguna lista, entonces:
			#agregarlo a $users_to_add
			$users_to_add << user_to_create
			#preguntar a todos los demás servidores (GET_UVG - ask_create_user)
			i=0
			can_create = true
			while i<$server_list.size
				nodo = $server_list[i]
				nodo.setRequest_to_GET_UVG($server_name)
				nodo.addHeader("Action","ask_create_user")
				nodo.addHeader("User","#{encode}")
				
				answer = nodo.sendRequest()
				
				if answer.upcase.include?("200 OK")
					
				elsif answer.upcase.include?("4400 CANT_CREATE_USER")
					can_create = false
					break
				elsif answer.upcase.include?("4403 PROTOCOL_ERROR")
					can_create = false
					break
				end
				i+=1
			end
			#solo mandar rollback a todos los que contestaron 200 OK
			j=0
			while j<i
				nodo = $server_list[j]
				nodo.setRequest_to_GET_UVG($server_name)
				if can_create
					#si respuesta = true -> mandar go_create_user
					nodo.addHeader("Action","go_create_user")
					nodo.addHeader("User","#{encode}")
				else
					#si respuesta = false -> mandar back_create_user
					nodo.addHeader("Action","back_create_user")
					nodo.addHeader("User","#{encode}")				
				end
				
				nodo.sendRequest()
				j+=1
			end
			#sacar de la lista de usuarios por agregar
			removeUser($users_to_add, encode)
			if can_create
				#si se puede crear, meterlo a la lista de usuarios
				$users << user_to_create
				@answer.setHTTP_code_Users("user ok","created","#{user_to_create.name}")
			else
				@answer.setHTTP_code_Users("user problem","create","#{user_to_create.name}")
			end
		end
	end
	
	#------------------------------------------------------
	# send_edit_user() -> envía a todos los servidores la
	# petición de borrar al usuario, si corresponde
	#------------------------------------------------------
	def send_edit_user(user,psw)
		
		#GET_UVG / HTTP/1.1
		#Host: {ServerName}/8080
		#Action: ask_update_user
		#User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
		
		user_to_update = User.new(user,psw)
		#puts "Usuario a crear: #{user_to_update.print()} = #{user_to_update.get_encode()}"#TODO quitar debug
		encode = user_to_update.get_encode()
		
		if !existUser($users,encode)
			#user to edit doesn't exist
			@answer.setHTTP_code_Users("user don't exist","edit","#{user_to_update.name}")
		elsif existUser($users_to_add,encode) or existUser($users_to_delete,encode) or existUser($users_to_update,encode)
			#user in proccess, cant edit
			@answer.setHTTP_code_Users("user progress","edit","#{user_to_update.name}")
		else
			#sí está en la lista de usuarios, entonces:
			#agregarlo a $users_to_update
			$users_to_update << user_to_update
			#preguntar a todos los demás servidores (GET_UVG - ask_update_user)
			i=0
			can_update = true
			while i<$server_list.size
				nodo = $server_list[i]
				nodo.setRequest_to_GET_UVG($server_name)
				nodo.addHeader("Action","ask_update_user")
				nodo.addHeader("User","#{encode}")
				
				answer = nodo.sendRequest()
				
				if answer.upcase.include?("200 OK")
					
				elsif answer.upcase.include?("4401 CANT_UPDATE_USER")
					can_update = false
					break
				elsif answer.upcase.include?("4403 PROTOCOL_ERROR")
					can_udpate = false
					break
				end
				i+=1
			end
			#solo mandar rollback a todos los que contestaron 200 OK
			j=0
			while j<i
				nodo = $server_list[j]
				nodo.setRequest_to_GET_UVG($server_name)
				if can_update
					#si respuesta = true -> mandar go_update_user
					nodo.addHeader("Action","go_update_user")
					nodo.addHeader("User","#{encode}")
				else
					#si respuesta = false -> mandar back_udpate_user
					nodo.addHeader("Action","back_update_user")
					nodo.addHeader("User","#{encode}")				
				end
				
				nodo.sendRequest()
				j+=1
			end
			#sacar de la lista de usuarios por agregar
			removeUser($users_to_update, encode)
			if can_update
				#si se puede editar, meterlo a la lista de usuarios
				removeUser($users,encode)
				$users << user_to_update
				@answer.setHTTP_code_Users("user ok","updated","#{user_to_update.name}")
			else
				@answer.setHTTP_code_Users("user problem","update","#{user_to_update.name}")
			end
		end
	end
	
	#------------------------------------------------------
	# send_delete_user() -> envía a todos los servidores la
	# petición de borrar al usuario, si corresponde
	#------------------------------------------------------
	def send_delete_user(user,psw)
		
		#GET_UVG / HTTP/1.1
		#Host: {ServerName}/8080
		#Action: ask_delete_user
		#User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
		
		user_to_delete = User.new(user,psw)
		#puts "Usuario a borrar: #{user_to_delete.print()} = #{user_to_delete.get_encode()}"#TODO quitar debug
		encode = user_to_delete.get_encode()
		
		if !existUser($users,encode)
			#user to delte doesn't exist
			@answer.setHTTP_code_Users("user don't exist","delete","#{user_to_delete.name}")
		elsif existUser($users_to_add,encode) or existUser($users_to_delete,encode) or existUser($users_to_update,encode)
			#user in proccess, cant edit
			@answer.setHTTP_code_Users("user progress","delete","#{user_to_delete.name}")
		else
			#sí está en la lista de usuarios, entonces:
			#agregarlo a $users_to_delete
			$users_to_delete << user_to_delete
			#preguntar a todos los demás servidores (GET_UVG - ask_delete_user)
			i=0
			can_delete = true
			while i<$server_list.size
				nodo = $server_list[i]
				nodo.setRequest_to_GET_UVG($server_name)
				nodo.addHeader("Action","ask_delete_user")
				nodo.addHeader("User","#{encode}")
				
				answer = nodo.sendRequest()
				
				if answer.upcase.include?("200 OK")
					
				elsif answer.upcase.include?("4402 CANT_DELETE_USER")
					can_delete = false
					break
				elsif answer.upcase.include?("4403 PROTOCOL_ERROR")
					can_delete = false
					break
				end
				i+=1
			end
			#solo mandar rollback a todos los que contestaron 200 OK
			j=0
			while j<i
				nodo = $server_list[j]
				nodo.setRequest_to_GET_UVG($server_name)
				if can_delete
					#si respuesta = true -> mandar go_update_user
					nodo.addHeader("Action","go_delete_user")
					nodo.addHeader("User","#{encode}")
				else
					#si respuesta = false -> mandar back_udpate_user
					nodo.addHeader("Action","back_delete_user")
					nodo.addHeader("User","#{encode}")				
				end
				
				nodo.sendRequest()
				j+=1
			end
			#sacar de la lista de usuarios por borrar
			removeUser($users_to_delete, encode)
			if can_delete
				#si se puede borrar, meterlo a la lista de usuarios
				removeUser($users,encode)
				@answer.setHTTP_code_Users("user ok","deleted","#{user_to_delete.name}")
			else
				@answer.setHTTP_code_Users("user problem","delete","#{user_to_delete.name}")
			end
		end
	end
	
	
	#------------------------------------------------------
	# analizeAction() -> analiza un GET_UVG con Action
	#------------------------------------------------------
	def analyzeAction()
		action = @headers.get("Action")
		
		puts "GET_UVG request - Action: .#{action}."#TODO quitar debug
		
		case action
		#-----------------------------------------------------
		when "ask_create_user"
			askCreateUser()
		when "go_create_user"
			goCreateUser(true)
		when "back_create_user"
			goCreateUser(false)
		#-----------------------------------------------------
		when "ask_update_user"
			askUpdateUser()
		when "go_update_user"
			goUpdateUser(true)
		when "back_update_user"
			goUpdateUser(false)
		#-----------------------------------------------------
		when "ask_delete_user"
			askDeleteUser()
		when "go_delete_user"
			goDeleteUser(true)
		when "back_delete_user"
			goDeleteUser(false)
		#-----------------------------------------------------
		when "ask_downtime"
			askDowntime()
		#-----------------------------------------------------
		when "update_file"
			updateFile()
		else
		#-----------------------------------------------------
			#error al enviar la petición ya que la action
			#no es ninguna del protocolo
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","GET_UVG - Action: action not defined in the protocol")
			
		end
	end
	
	
	#------------------------------------------------------
	# askDowntime()
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: ask_downtime
	#------------------------------------------------------
	def askDowntime()
		
		#HTTP/1.1 200 OK
		#Host: {ServerName}/8080
		#Status: [working | waiting | down]
		
		@answer.setHTTP_code("ok")
		@answer.addHeader("Status","#{$status}")
	end
	
	#------------------------------------------------------
	# updateFile()
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: update_file
	#	Name: {FileName}
	#	File: {FileContent}
	#------------------------------------------------------
	def updateFile()
		
		if @headers.contain("Host") and @headers.contain("Name") and @headers.contain("File")
		
			host = @headers.get("Host").split(":")[0]
			file_path = @headers.get("Name")
			content = @headers.get("File")
			
			puts "Actualizando: #{host} - #{file_path} - .#{content}."
			
			$cache.flush(file_path, host, content) #path, owner, content
			@answer.setHTTP_code("ok")
			
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action update_file without all headers (Needs 'Host', 'Name' and 'File'")
		end
		
	end
	
	#------------------------------------------------------
	# askCreateUser()
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: ask_create_user
	#	User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	#------------------------------------------------------
	def askCreateUser()
		
		if @headers.contain("User")
			user = @headers.get("User")
			#revisar si ya existe dicho usuario en alguna lista
			if existUser($users,user) or existUser($users_to_add,user) or existUser($users_to_delete,user) or existUser($users_to_update,user)
				#+++++++++++++++++++++++++++++++++++++++++++
				#si sí se encuentra, no meterlo y devolver que no se puede
				@answer.setHTTP_code("cant create user")
				#+++++++++++++++++++++++++++++++++++++++++++
			else
				#+++++++++++++++++++++++++++++++++++++++++++
				#si no se encuentra, meterlo y devolver OK
				$users_to_add << User.new(user)
				@answer.setHTTP_code("ok")
				#+++++++++++++++++++++++++++++++++++++++++++
			end
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action ask_create_user without header 'User'")
		end
		
	end
	
	#------------------------------------------------------
	# goCreateUser(crear)
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: go_create_user | back_create_user
	#	User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	#------------------------------------------------------
	def goCreateUser(crear)
		if @headers.contain("User")
			#sí tiene el header user
			user = @headers.get("User")
			if existUser($users_to_add,user)
				#sí existe el usuario.. entonces ver de removerlo
				if crear
					#go_create_user => meterlo a la lista de usuarios
					user_to_add = removeUser($users_to_add,user)
					puts "se ha creado el nuevo usuario: #{user_to_add.print()}"#TODO quitar debug
					$users << user_to_add
				else
					#back_create_user => sólo removerlo
					removeUser($users_to_add,user)
				end
				@answer.setHTTP_code("ok")
			else
				#Action de goCreateUser sin que exista el usuario en to_add
				@answer.setHTTP_code("error")
				@answer.addHeader("Problem","goCreateUser without the User in users_to_add_list")
			end
			
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action ask_create_user without header 'User'")
		end
	end
	
	#------------------------------------------------------
	# askUpdateUser()
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: ask_update_user
	#	User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	#------------------------------------------------------
	def askUpdateUser()
		
		if @headers.contain("User")
			user = @headers.get("User")
			#el usuario tiene que existir en $users, pero NO en ninguna otra lista
			if !existUser($users,user) or existUser($users_to_add,user) or existUser($users_to_delete,user) or existUser($users_to_update,user)
				#+++++++++++++++++++++++++++++++++++++++++++
				#si sí se encuentra, no meterlo y devolver que no se puede
				@answer.setHTTP_code("cant update user")
				#+++++++++++++++++++++++++++++++++++++++++++
			else
				#+++++++++++++++++++++++++++++++++++++++++++
				#si no se encuentra, meterlo y devolver OK
				$users_to_update << User.new(user)
				@answer.setHTTP_code("ok")
				#+++++++++++++++++++++++++++++++++++++++++++
			end
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action ask_update_user without header 'User'")
		end
		
	end
	
	#------------------------------------------------------
	# goUpdateUser(actualizar)
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: go_update_user | back_update_user
	#	User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	#------------------------------------------------------
	def goUpdateUser(actualizar)
		if @headers.contain("User")
			#sí tiene el header user
			user = @headers.get("User")
			if existUser($users_to_update,user) and existUser($users,user)
				#sí existe el usuario.. entonces ver de removerlo
				if actualizar
					#go_update_user => meterlo a la lista de usuarios
					#remover el anterior de la lista de usuarios y agregar el nuevo
					user_to_update = removeUser($users_to_update,user)
					removeUser($users,user)
					$users << user_to_update
					puts "se ha actualizado al usuario: #{user_to_update.print()}"#TODO quitar debug
				else
					#back_update_user => sólo removerlo
					removeUser($users_to_update,user)
				end
				@answer.setHTTP_code("ok")
			else
				#Action de goUpdateUser sin que exista el usuario en to_update
				@answer.setHTTP_code("error")
				@answer.addHeader("Problem","goUpdateUser without the User in users_to_update_list or users_list")
			end
			
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action ask_update_user without header 'User'")
		end
	end


	#------------------------------------------------------
	# askDeleteUser()
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: ask_delete_user
	#	User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	#------------------------------------------------------
	def askDeleteUser()
		
		if @headers.contain("User")
			user = @headers.get("User")
			#el usuario tiene que existir en $users, pero NO en ninguna otra lista
			if !existUser($users,user) or existUser($users_to_add,user) or existUser($users_to_delete,user) or existUser($users_to_update,user)
				#+++++++++++++++++++++++++++++++++++++++++++
				#si sí se encuentra, no meterlo y devolver que no se puede
				@answer.setHTTP_code("cant delete user")
				#+++++++++++++++++++++++++++++++++++++++++++
			else
				#+++++++++++++++++++++++++++++++++++++++++++
				#si no se encuentra, meterlo y devolver OK
				$users_to_delete << User.new(user)
				@answer.setHTTP_code("ok")
				#+++++++++++++++++++++++++++++++++++++++++++
			end
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action ask_delete_user without header 'User'")
		end
		
	end
	
	#------------------------------------------------------
	# goDeleteUser(borrar)
	#	GET_UVG / HTTP/1.1
	#	Host: {ServerName}/8080
	#	Action: go_delete_user | back_delete_user
	#	User: QWxhZGRpbjpvcGVuIHNlc2FtZQ==
	#------------------------------------------------------
	def goDeleteUser(borrar)
		if @headers.contain("User")
			#sí tiene el header user
			user = @headers.get("User")
			if existUser($users_to_delete,user) and existUser($users,user)
				#sí existe el usuario.. entonces ver de removerlo
				if borrar
					#go_delete_user => meterlo a la lista de usuarios
					#remover de la lista de users y de users_to_delete
					user_to_delete = removeUser($users_to_delete,user)
					removeUser($users,user)
					puts "se ha eliminado al usuario: #{user_to_delete.print()}"#TODO quitar debug
				else
					#back_delete_user => sólo removerlo
					removeUser($users_to_delete,user)
				end
				@answer.setHTTP_code("ok")
			else
				#Action de goDeleteUser sin que exista el usuario en to_delete
				@answer.setHTTP_code("error")
				@answer.addHeader("Problem","goDeleteUser without the User in users_to_delete_list or users_list")
			end
			
		else
			#Action de create_user sin header "User"
			@answer.setHTTP_code("error")
			@answer.addHeader("Problem","Action ask_delete_user without header 'User'")
		end
	end


	
	
	
	
	
	#------------------------------------------------------
	# removeUser()
	# retorna el usuario removido de determinada lista
	#------------------------------------------------------
	def removeUser(user_list, user_to_find)
		user_list.each{ |i|
			if i.isUser(user_to_find)
				user_list.delete(i)
				return i
			end
		}
		return nil
	end
	
	
	#------------------------------------------------------
	# existUserAndPsw()
	# retorna si en la lista "user_list" existe el usuario
	# "user_to_find" con su respectiva contraseña
	#------------------------------------------------------
	def existUserAndPsw(user_list, user_to_find)
		user_list.each{ |i|
			if i.isUserAndPsw(user_to_find)
				return true
			end
		}
		return false
	end
	
	#------------------------------------------------------
	# existUser()
	# retorna si en la lista "user_list" existe el usuario
	# "user_to_find"
	#------------------------------------------------------
	def existUser(user_list, user_to_find)
		user_list.each{ |i|
			if i.isUser(user_to_find)
				return true
			end
		}
		return false
	end


	#------------------------------------------------------
	# sendAnswer() -> envía la respuesta al cliente
	#------------------------------------------------------
	def sendAnswer()
		saveAccessLog(@client_IP, @url, @answer.HTTP_code, @answer.content.length, @get_from,@request.chomp!, @authorization_txt)
		@client.puts @answer.answer()
	end	
	
	#------------------------------------------------------
	# close() -> cierra la conexión con el cliente
	#------------------------------------------------------
	def close()
		@client.close
	end

end

#======================================================================================================================================================



###############################################################
# print_logo
# sirve para imprimir el logo del inicio
###############################################################
def print_logo()
	temp = "\n            _             _     ___                                      " << "\n"
	temp << "   _ __    (_)   _ _     (_)   / __|   ___     _ _  __ __   ___     _ _  " << "\n"
	temp << "  | '  \\   | |  | ' \\    | |   \\__ \\  / -_)   | '_| \\ V /  / -_)   | '_| " << "\n"
	temp << "  |_|_|_| _|_|_ |_||_|  _|_|_  |___/  \\___|  _|_|_  _\\_/_  \\___|  _|_|_  " << "\n"
	temp << "__|\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"||\"\"\"\"\"|__" << "\n"
	temp << "  `-0-0-'`-0-0-'`-0-0-'`-0-0-'`-0-0-'`-0-0-'`-0-0-'`-0-0-'`-0-0-'`-0-0-'" << "\n"
	temp << ""
	puts temp
	return temp
end

###############################################################
# saveAccessLog
# sirve para guardar el log de una conexión con algún cliente
###############################################################
def saveAccessLog(client_IP, url, http_code, bytes_len, from, request, authorization_txt)
	temp = ("#"*60)+"\n"
	temp << ("* Solicitud hecha: #{request}") << "\n"
	temp << ("* Hora y fecha de acceso: "+Time.now.asctime) << "\n"
	temp << ("* IP del cliente: #{client_IP}") << "\n"
	temp << ("* URL solicitada: #{url}") << "\n"
	temp << ("#{authorization_txt}") << "\n"
	temp << ("* Código HTTP resultado:\n\n#{http_code}\n") << "\n"
	temp << ("* Bytes transferidos: #{bytes_len}") << "\n"
	temp << ("* Leido desde: #{from}")
	temp << "\n"
	temp << ("#"*60)+"\n"
	
	$log.info "\n"+temp+"\n"
	
	puts temp	#TODO quitar este debug..
end

###############################################################
# ----------------- inicio de la ejecución --------------------
###############################################################
$cache = Cache.new($max_cache)


temp = "Configuración del servidor:"+"\n"
temp << "\t"+"* Puerto a escuchar: #{$port}"+"\n"
temp << "\t"+"* Tamaño de caché: #{$max_cache}"+"\n"
temp << "\t"+"* Directorio base: #{$root}"+"\n"
temp << "\t"+"* Server Name: #{$server_name}"+"\n"
temp << "\t"+"* IP: #{local_ip()}"+"\n"
puts temp

$log.info "\n"+print_logo()+"\n"

$log.info "\n"+temp+"\n"

$server = TCPServer.new($port)


loop do
	#Thread.start($server.accept) do |client|
		client = $server.accept #TODO comentar esto si se pone el Thread
		conection = Conection.new(client)
		
		conection.analyzeRequest()
		#conection.sendAnswer()
		conection.close()
	#end
end
